/*
 * Copyright (C) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package github.hellocsl.layoutmanager.gallery;

import ohos.agp.components.Component;
import ohos.agp.components.PageSlider;
import ohos.agp.components.DirectionalLayout;
import ohos.agp.components.PageSliderProvider;
import ohos.agp.components.ComponentContainer;
import ohos.agp.components.AttrHelper;
import ohos.agp.components.StackLayout;
import ohos.agp.components.Image;
import ohos.agp.components.AttrSet;
import ohos.agp.render.Canvas;
import ohos.agp.utils.LayoutAlignment;
import ohos.app.Context;
import ohos.multimodalinput.event.MmiPoint;
import ohos.multimodalinput.event.TouchEvent;

import java.util.List;

/**
 * 轮播图
 *
 * @since 2021-08-09
 */
public class Banner extends PageSlider implements PageSlider.PageChangedListener,
        Component.TouchEventListener, Component.DrawTask {
    private static final int CONSTANT_1 = 1;
    private static final int CONSTANT_2 = 2;
    private static final float CONSTANT_2F = 2f;
    private static final int CONSTANT_5 = 5;

    // 方向布局，组容器
    private DirectionalLayout groupContainer;

    // 当前容器的索引
    private int currentContainerIndex = 0;

    // 新的容器的索引（用于更新当前容器索引）
    private int newCurrentContainerIndex = 0;

    // 按下x轴坐标
    private float downX;

    // 是否第一次滑动
    private boolean isFirstSlide = true;

    // 是否滑入上一页
    private boolean isSlidePreviousPage = false;

    // 是否右往左移动
    private boolean isRightToLeft = false;

    // 是否滑入下一页
    private boolean isSlideNextPage = false;

    // 页面滑动状态改变：0空闲、1拖动、2滑动
    private int slideState = 0;

    // 是否初始化当前容器索引
    private boolean isInitCurrentContainerIndex = false;

    // 是否初始化后，重新设置setCurrentPage
    private boolean isSetCurrentPage = false;

    // 旧的当前页面
    private int oldCurrentPage = 0;

    // 当前页面
    private int currentPageIndex = 0;

    // 页面间距
    private int pageMargin = 0;

    // 页面切换动画
    private PageTransformer pageTransformer;

    // 页面点击事件
    private PageClickedListener pageClickedListener;

    // 页面选中事件
    private PageSelectedListener pageSelectedListener;

    // 按下的x轴
    private float clickDownX = 0;

    /**
     * 构造方法
     *
     * @param context context
     */
    public Banner(Context context) {
        this(context, null);
    }

    /**
     * 构造方法
     *
     * @param context context
     * @param attrSet attrSet
     */
    public Banner(Context context, AttrSet attrSet) {
        this(context, attrSet, "");
    }

    /**
     * 构造方法
     *
     * @param context context
     * @param attrSet attrSet
     * @param styleName styleName
     */
    public Banner(Context context, AttrSet attrSet, String styleName) {
        super(context, attrSet, styleName);
        addDrawTask(this);
        setTouchEventListener(this);
        addPageChangedListener(this);
    }

    /**
     * 设置界面切换动画
     *
     * @param pageTransformer 界面切换动画
     */
    public void setPageTransformer(PageTransformer pageTransformer) {
        this.pageTransformer = pageTransformer;
    }

    /**
     * 设置页面点击事件
     *
     * @param pageClickedListener 页面点击事件
     */
    public void setPageClickedListener(PageClickedListener pageClickedListener) {
        this.pageClickedListener = pageClickedListener;
    }

    /**
     * 设置页面选中事件
     *
     * @param pageSelectedListener 页面选中事件
     */
    public void setPageSelectedListener(PageSelectedListener pageSelectedListener) {
        this.pageSelectedListener = pageSelectedListener;
    }

    /**
     * 设置数据
     *
     * @param dataList 数据集合
     * @param width 宽度
     * @param height 高度
     */
    public void setData(List<Integer> dataList, int width, int height) {
        setProvider(new PageSliderProvider() {
            @Override
            public int getCount() {
                return dataList.size();
            }

            @Override
            public Object createPageInContainer(ComponentContainer container, int position) {
                Image image = new Image(getContext());
                StackLayout.LayoutConfig layoutConfig;
                if (width != 0) {
                    layoutConfig = new StackLayout.LayoutConfig(width, height);
                    image.setScaleMode(Image.ScaleMode.CLIP_CENTER);
                } else {
                    layoutConfig = new StackLayout.LayoutConfig(ComponentContainer.LayoutConfig.MATCH_PARENT,
                            ComponentContainer.LayoutConfig.MATCH_PARENT);
                    image.setScaleMode(Image.ScaleMode.STRETCH);
                }
                layoutConfig.alignment = LayoutAlignment.CENTER;
                image.setLayoutConfig(layoutConfig);
                image.setPixelMap(dataList.get(position));
                image.setMarginsLeftAndRight(pageMargin / CONSTANT_2, pageMargin / CONSTANT_2);
                image.setCornerRadius(AttrHelper.vp2px(CONSTANT_5, getContext()));
                container.addComponent(image);
                return image;
            }

            @Override
            public void destroyPageFromContainer(ComponentContainer container, int position, Object object) {
                container.removeComponent((Component) object);
            }

            @Override
            public boolean isPageMatchToObject(Component page, Object object) {
                return true;
            }
        });
    }

    @Override
    public void setPageMargin(int margin) {
        if (margin < 0) {
            pageMargin = Math.abs(margin);
        }
        super.setPageMargin(margin);
    }

    @Override
    public boolean onTouchEvent(Component component, TouchEvent touchEvent) {
        MmiPoint pointerPosition = touchEvent.getPointerPosition(touchEvent.getIndex());
        switch (touchEvent.getAction()) {
            case TouchEvent.PRIMARY_POINT_DOWN:
                downX = pointerPosition.getX();
                break;
            case TouchEvent.POINT_MOVE:
                float value = pointerPosition.getX() - downX;
                isSlidePreviousPage = value > 0;
                break;
            default:
                break;
        }
        return true;
    }

    @Override
    public void onDraw(Component component, Canvas canvas) {
        if (groupContainer == null) {
            groupContainer = (DirectionalLayout) getComponentAt(0);
            initCurrentContainerIndex();
            setItemTransformer(0, 0);
            if (pageSelectedListener != null) {
                Component componentAt = groupContainer.getComponentAt(newCurrentContainerIndex);
                pageSelectedListener.onSelect(componentAt, getCurrentPage());
            }
            setPageClicked();
            currentPageIndex = getCurrentPage();
        }
    }

    @Override
    public void setCurrentPage(int itemPos) {
        oldCurrentPage = getCurrentPage();
        isSetCurrentPage = true;
        super.setCurrentPage(itemPos);
    }

    @Override
    public void setCurrentPage(int itemPos, boolean isSmoothScroll) {
        oldCurrentPage = getCurrentPage();
        isSetCurrentPage = true;
        super.setCurrentPage(itemPos, isSmoothScroll);
    }

    /**
     * 初始化之后设置当前页数，更新容器索引
     *
     * @param itemPos 当前页数
     */
    private void setCurrentPageUpdateIndex(int itemPos) {
        if (isInitCurrentContainerIndex) {
            // 传的页面和当前页面比较
            // 大于：当前容器索引+1
            // 小于：当前容器索引-1
            if (itemPos > oldCurrentPage) {
                newCurrentContainerIndex++;
                if (newCurrentContainerIndex > groupContainer.getChildCount() - 1) {
                    newCurrentContainerIndex = 0;
                }
            } else if (itemPos < oldCurrentPage) {
                lessThanOldCurrentPage(itemPos);
            }
        }
    }

    private void lessThanOldCurrentPage(int itemPos) {
        if (oldCurrentPage == getProvider().getCount() - 1 && itemPos == 0) {
            newCurrentContainerIndex++;
            if (newCurrentContainerIndex > groupContainer.getChildCount() - 1) {
                newCurrentContainerIndex = 0;
            }
        } else {
            newCurrentContainerIndex--;
            if (newCurrentContainerIndex < 0) {
                newCurrentContainerIndex = groupContainer.getChildCount() - 1;
            }
        }
    }

    /**
     * 初始化当前容器索引
     */
    private void initCurrentContainerIndex() {
        if (groupContainer == null) {
            return;
        }
        int currentPage = getCurrentPage();
        int cachedPages = getCachedPagesLimit();
        int containerCount = groupContainer.getChildCount();
        int dataCount = getProvider().getCount();
        int ccIndex = 0;

        // 是否开启了无限循环
        if (isCircularModeEnabled()) {
            // 当前页就是缓存的数量
            ccIndex = cachedPages;

            // 设置了选中页面
            if (currentPage != 0 && currentPage < dataCount) {
                if (currentPage == dataCount - 1) {
                    ccIndex = cachedPages - 1;
                } else {
                    ccIndex = cachedPages + 1;
                }
            }
        } else {
            // 未开启循环，设置选中页面
            if (currentPage != 0 && currentPage < dataCount) {
                if (currentPage < containerCount + cachedPages) {
                    ccIndex = currentPage % containerCount;
                } else if (currentPage < dataCount - cachedPages) {
                    ccIndex = cachedPages;
                } else {
                    ccIndex = cachedPages + currentPage - (dataCount - 1 - cachedPages);
                }
            }
        }
        currentContainerIndex = ccIndex;
        newCurrentContainerIndex = currentContainerIndex;
        isInitCurrentContainerIndex = true;
    }

    /**
     * 页面滑动中事件
     *
     * @param itemPos 数据索引
     * @param itemPosOffset 偏移量
     * @param itemPosOffsetPixels 偏移像素
     */
    @Override
    public void onPageSliding(int itemPos, float itemPosOffset, int itemPosOffsetPixels) {
        // 设置页面切换动画
        if (pageTransformer != null && groupContainer != null) {
            // 子容器宽度
            int childWidth = getWidth() - pageMargin;

            // （首次滑动bug）因为开启了无限循环，itemPosOffsetPixels包括了前面的缓存页面
            // 首次下一页，itemPosOffset一直为1.0 且 itemPosOffsetPixels大于容器的宽度
            // 首次上一页，itemPosOffsetPixels为正数
            float offset = itemPosOffset;
            int offsetPixels = itemPosOffsetPixels;
            if (Math.abs(offsetPixels) > childWidth
                    || (isFirstSlide && isSlidePreviousPage && offsetPixels > 0)) {
                offsetPixels = Math.abs(offsetPixels) - childWidth * getCachedPagesLimit();
                offset = Math.abs((float) offsetPixels / childWidth);
            }

            if (isRightToLeft && offsetPixels < 0) {
                offsetPixels = Math.abs(offsetPixels);
            }

            // 非滑动状态，更新当前索引
            if (slideState != CONSTANT_2) {
                // 偏移像素，正数-下一页，负数-上一页
                isSlideNextPage = offsetPixels > 0;
                currentContainerIndex = newCurrentContainerIndex;
            } else {
                isFirstSlide = false;
            }

            if (isSlideNextPage) {
                offsetPixels = CONSTANT_1;
            } else {
                offsetPixels = -CONSTANT_1;
            }

            setItemTransformer(offset, offsetPixels);
        }
    }

    /**
     * 加载页面切换动画
     *
     * @param offset 偏移量
     * @param offsetPixels 偏移像素
     */
    private void setItemTransformer(float offset, float offsetPixels) {
        if (pageTransformer == null || groupContainer == null) {
            return;
        }

        // 上页索引
        int previousPageIndex;
        if (currentContainerIndex - 1 < 0) {
            previousPageIndex = groupContainer.getChildCount() - 1;
        } else {
            previousPageIndex = currentContainerIndex - 1;
        }

        // 下页索引
        int nextPageIndex;
        if (currentContainerIndex + 1 > groupContainer.getChildCount() - 1) {
            nextPageIndex = 0;
        } else {
            nextPageIndex = currentContainerIndex + 1;
        }

        for (int index = 0; index < groupContainer.getChildCount(); index++) {
            Component item = groupContainer.getComponentAt(index);
            PageType pageType;
            if (previousPageIndex == index) {
                pageType = PageType.PREVIOUS;
            } else if (currentContainerIndex == index) {
                pageType = PageType.CURRENT;
            } else if (nextPageIndex == index) {
                pageType = PageType.NEXT;
            } else {
                pageType = PageType.OTHER;
            }
            pageTransformer.transformerPage(pageType, item, offset, offsetPixels);
        }
    }

    /**
     * 页面滑动状态改变
     *
     * @param state 0空闲、1拖动、2滑动
     */
    @Override
    public void onPageSlideStateChanged(int state) {
        slideState = state;
        if (slideState != CONSTANT_2) {
            currentContainerIndex = newCurrentContainerIndex;
            setPageClicked();
        }
    }

    /**
     * 获取滑动状态名
     *
     * @param state 滑动状态
     * @return 滑动状态名
     */
    private String getSlideStateName(int state) {
        String stateName;
        switch (state) {
            case 0:
                stateName = "0，空闲";
                break;
            case 1:
                stateName = "1，拖动";
                break;
            default:
                stateName = "2，滑动";
                break;
        }
        return stateName;
    }

    /**
     * 选择新页面成功时回调
     *
     * @param itemPos 数据索引
     */
    @Override
    public void onPageChosen(int itemPos) {
        isRightToLeft = false;
        if (groupContainer == null) {
            isSetCurrentPage = false;
            return;
        }

        // 重新设置新的页面，更新当前容器索引
        if (isSetCurrentPage) {
            isSetCurrentPage = false;
            setCurrentPageUpdateIndex(itemPos);
        } else {
            isSlideNextPage = itemPos > currentPageIndex;

            // 手动左右滑动，更新当前容器索引
            if (isSlideNextPage) {
                newCurrentContainerIndex++;
                if (newCurrentContainerIndex > groupContainer.getChildCount() - 1) {
                    newCurrentContainerIndex = 0;
                }
            } else {
                newCurrentContainerIndex--;
                if (newCurrentContainerIndex < 0) {
                    newCurrentContainerIndex = groupContainer.getChildCount() - 1;
                }
            }
        }

        // 页面选中事件
        if (pageSelectedListener != null) {
            pageSelectedListener.onSelect(groupContainer.getComponentAt(newCurrentContainerIndex), itemPos);
        }
        currentPageIndex = itemPos;
    }

    /**
     * 一页三屏，页面是后面容器盖在前面容器，
     * 导致只能得到 当前容器和后面容器的点击事件
     * 设置点击事件，上页，当前页，下页
     */
    private void setPageClicked() {
        if (groupContainer == null) {
            return;
        }

        // 页面点击事件
        if (pageClickedListener != null) {
            int itemPos = getCurrentPage();
            int containerCount = groupContainer.getChildCount();
            int dataCount = getProvider().getCount();
            int ccIndex = currentContainerIndex;

            // 下页容器索引
            int nextPageIndex;
            if (ccIndex + 1 > containerCount - 1) {
                nextPageIndex = 0;
            } else {
                nextPageIndex = ccIndex + 1;
            }

            int previousItemPos = getPreviousItemPos();
            int nextPageItemPos = getNextPageItemPos();

            for (int index = 0; index < containerCount; index++) {
                Component child = groupContainer.getComponentAt(index);
                if (pageMargin > 0) {
                    if (ccIndex == index) {
                        itemTouchListener(itemPos, dataCount, previousItemPos, child);
                    } else if (nextPageIndex == index) {
                        itemNextTouchListener(itemPos, nextPageItemPos,child);
                    } else {
                        child.setTouchEventListener((component, touchEvent) -> false);
                    }
                } else {
                    if (ccIndex == index) {
                        child.setClickedListener(component -> {
                            pageClickedListener.onClick(component, itemPos);
                        });
                    } else {
                        child.setClickedListener(component -> {
                        });
                    }
                }
            }
        }
    }

    /**
     * 获取上页数据索引
     *
     * @return 上页数据索引
     */
    private int getPreviousItemPos() {
        int previousItemPos;
        if (getCurrentPage() - 1 < 0) {
            previousItemPos = getProvider().getCount() - 1;
        } else {
            previousItemPos = getCurrentPage() - 1;
        }
        return previousItemPos;
    }

    /**
     * 获取下页数据索引
     *
     * @return 下页数据索引
     */
    private int getNextPageItemPos() {
        int nextPageItemPos;
        if (getCurrentPage() + 1 > getProvider().getCount() - 1) {
            nextPageItemPos = 0;
        } else {
            nextPageItemPos = getCurrentPage() + 1;
        }
        return nextPageItemPos;
    }

    private void itemTouchListener(int itemPos, int dataCount, int previousItemPos, Component child) {
        child.setTouchEventListener(new TouchEventListener() {
            @Override
            public boolean onTouchEvent(Component component, TouchEvent touchEvent) {
                MmiPoint pointerPosition = touchEvent.getPointerPosition(touchEvent.getIndex());
                switch (touchEvent.getAction()) {
                    case TouchEvent.PRIMARY_POINT_DOWN:
                        clickDownX = pointerPosition.getX();
                        break;
                    case TouchEvent.PRIMARY_POINT_UP:
                        if (pointerPosition.getX() == clickDownX) {
                            float pageMarginCentre = pageMargin / CONSTANT_2F;
                            if (pageMarginCentre < clickDownX
                                    && getWidth() - pageMarginCentre > clickDownX) {
                                pageClickedListener.onClick(component, itemPos);
                            } else if (pageMarginCentre > clickDownX) {
                                if (!isCircularModeEnabled() && previousItemPos != dataCount - 1) {
                                    pageClickedListener.onClick(component, previousItemPos);
                                }
                            }
                        }
                        break;
                    case TouchEvent.POINT_MOVE:
                        float value = pointerPosition.getX() - clickDownX;
                        isRightToLeft = value < 0;
                        break;
                    default:
                        break;
                }
                return true;
            }
        });
    }

    private void itemNextTouchListener(int itemPos, int nextItemPos, Component child) {
        child.setTouchEventListener(new TouchEventListener() {
            @Override
            public boolean onTouchEvent(Component component, TouchEvent touchEvent) {
                MmiPoint pointerPosition = touchEvent.getPointerPosition(touchEvent.getIndex());
                switch (touchEvent.getAction()) {
                    case TouchEvent.PRIMARY_POINT_DOWN:
                        clickDownX = pointerPosition.getX();
                        break;
                    case TouchEvent.PRIMARY_POINT_UP:
                        if (pointerPosition.getX() == clickDownX) {
                            float pageMarginCentre = pageMargin / CONSTANT_2F;
                            if (pageMarginCentre > clickDownX) {
                                pageClickedListener.onClick(component, itemPos);
                            } else {
                                pageClickedListener.onClick(component, nextItemPos);
                            }
                        }
                        break;
                    case TouchEvent.POINT_MOVE:
                        float value = pointerPosition.getX() - clickDownX;
                        isRightToLeft = value < 0;
                        break;
                    default:
                        break;
                }
                return true;
            }
        });
    }

    /**
     * 页面点击事件
     *
     * @since 2021-08-09
     */
    public interface PageClickedListener {
        /**
         * 页面点击事件
         *
         * @param component component
         * @param itemPos itemPos
         */
        void onClick(Component component, int itemPos);
    }

    /**
     * 页面选中事件
     *
     * @since 2021-08-09
     */
    public interface PageSelectedListener {
        /**
         * 页面选中事件
         *
         * @param component component
         * @param itemPos itemPos
         */
        void onSelect(Component component, int itemPos);
    }

    /**
     * 页面切换动画
     *
     * @since 2021-08-09
     */
    public interface PageTransformer {
        /**
         * 页面切换动画实现
         *
         * @param pageType 页面类型：PREVIOUS、CURRENT、NEXT、OTHER
         * @param page 组件
         * @param offset 偏移量
         * @param offsetPixels 偏移像素
         */
        void transformerPage(PageType pageType, Component page, float offset, float offsetPixels);
    }

    /**
     * 页面类型
     *
     * @since 2021-08-09
     */
    public enum PageType {
        /**
         * 上页
         */
        PREVIOUS,
        /**
         * 当前页
         */
        CURRENT,
        /**
         * 下页
         */
        NEXT,
        /**
         * 其他
         */
        OTHER,
    }
}

